package com.whfc.emp.mqtt;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.whfc.common.constant.QueueConst;
import com.whfc.common.face.szyc.*;
import com.whfc.common.file.FileHandler;
import com.whfc.common.file.FilePathConfig;
import com.whfc.common.util.Base64Util;
import com.whfc.common.util.DateUtil;
import com.whfc.common.util.JSONUtil;
import com.whfc.common.util.RandomUtil;
import com.whfc.emp.dao.AppEmpMapper;
import com.whfc.emp.dao.AppFaceGatePersonMapper;
import com.whfc.emp.dto.AppFaceGateDTO;
import com.whfc.emp.entity.AppEmp;
import com.whfc.emp.entity.AppFaceGatePerson;
import com.whfc.emp.enums.EmpSyncImgType;
import com.whfc.emp.enums.FaceGateSyncOp;
import com.whfc.emp.enums.TaskType;
import com.whfc.emp.manager.AppFaceGateManager;
import com.whfc.emp.param.EmpAttendSyncDataParam;
import com.whfc.emp.param.EmpInfoSyncDataParam;
import com.whfc.emp.redis.FaceGateAttendRedisDao;
import com.whfc.emp.redis.MqttEmpRedisDao;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;

import java.io.ByteArrayInputStream;
import java.text.MessageFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.concurrent.locks.LockSupport;
import java.util.stream.Collectors;

/**
 * MQTT 人员闸机消息订阅处理
 *
 * @author qzexing
 * @version 1.0
 * @date 2021/12/9 16:06
 */
@Component
public class MqttMessageReceiverListener implements ApplicationListener<MqttReceiverEvent> {

    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    @Autowired
    private AppFaceGateManager appFaceGateManager;

    @Autowired
    private FaceGateAttendRedisDao faceGateAttendRedisDao;

    @Autowired
    private FilePathConfig filePathConfig;

    @Autowired
    private FileHandler fileHandler;

    @Autowired
    private AmqpTemplate amqpTemplate;

    @Autowired
    private MqttEmpRedisDao mqttEmpRedisDao;

    @Autowired
    private AppEmpMapper appEmpMapper;

    @Autowired
    private AppFaceGatePersonMapper appFaceGatePersonMapper;

    /**
     * 同一个人同一个闸机刷脸时间间隔大于1分钟
     */
    private static final Integer INTERVAL_TIME = 1;

    @Override
    public void onApplicationEvent(MqttReceiverEvent mqttReceiverEvent) {
        logger.info("MQTT事件监听器接收到消息");
        String message = mqttReceiverEvent.getMessage();
        JSONObject jsonObject = JSON.parseObject(message);
        String operator = jsonObject.getString(SzycConst.OPERATOR);
        logger.info("operator:{}", operator);
        if (SzycConst.OP_QUERY_PERSON_ACK.equals(operator)) {
            //查询人员列表
            Person person = jsonObject.getObject(SzycConst.INFO, Person.class);
            handlerPersonList(person);
        } else if (SzycConst.OP_SEARCH_PERSON_ACK.equals(operator)) {
            //查询人员信息
            PersonInfo personInfo = jsonObject.getObject(SzycConst.INFO, PersonInfo.class);
            String pic = jsonObject.getString("pic");
            handlerPersonInfo(personInfo, pic);
        } else if (SzycConst.OP_RECPUSH.equals(operator)) {
            //考勤记录
            RecRecord recRecord = jsonObject.getObject(SzycConst.INFO, RecRecord.class);
            handlerRecRecord(recRecord);
        } else if (SzycConst.OP_ADD_PERSONS_ACK.equals(operator)) {
            //批量添加人员
            AddPersonsResult result = jsonObject.getObject(SzycConst.INFO, AddPersonsResult.class);
            handlerAddPersonResult(result);
        } else if (SzycConst.OP_DEL_PERSONS_ACK.equals(operator)) {
            DelPersonsResult result = jsonObject.getObject(SzycConst.INFO, DelPersonsResult.class);
            handlerDelPersonResult(result);
        }
    }

    /**
     * 批量取消授权
     *
     * @param result 取消授权结果
     */
    private void handlerDelPersonResult(DelPersonsResult result) {
        String deviceKey = result.getFacesluiceId();
        List<PersonInfo> delSucInfo = result.getDelSucInfo();
        for (PersonInfo personInfo : delSucInfo) {
            AppFaceGatePerson faceGatePerson = appFaceGatePersonMapper.selectByPersonGuidAndDeviceKey(personInfo.getCustomId(), deviceKey);
            if (faceGatePerson != null) {
                appFaceGatePersonMapper.deleteByPrimaryKey(faceGatePerson.getId());
            }
        }
    }

    /**
     * 批量授权人员处理
     *
     * @param result 人员授权结果
     */
    private void handlerAddPersonResult(AddPersonsResult result) {
        String deviceKey = result.getFacesluiceId();
        //添加成功的人员列表
        List<AddPersonState> addSucInfo = result.getAddSucInfo();
        for (AddPersonState addPersonState : addSucInfo) {
            AppFaceGatePerson faceGatePerson = appFaceGatePersonMapper
                    .selectByPersonGuidAndDeviceKey(addPersonState.getCustomId(), deviceKey);
            if (faceGatePerson == null) {
                continue;
            }
            faceGatePerson.setTaskType(TaskType.EMP_AUTH.getValue());
            faceGatePerson.setMessage(null);
            appFaceGatePersonMapper.updateByPrimaryKeySelective(faceGatePerson);
        }
        //添加失败的人员列表
        List<AddPersonState> addErrInfo = result.getAddErrInfo();
        for (AddPersonState addPersonState : addErrInfo) {
            AppFaceGatePerson faceGatePerson = appFaceGatePersonMapper
                    .selectByPersonGuidAndDeviceKey(addPersonState.getCustomId(), deviceKey);
            if (faceGatePerson == null) {
                continue;
            }
            faceGatePerson.setTaskType(TaskType.EMP_AUTH_ERROR.getValue());
            faceGatePerson.setMessage(SzycErrorCodeUtils.parseAddPersonsErrorCode(addPersonState.getErrcode()));
            appFaceGatePersonMapper.updateByPrimaryKeySelective(faceGatePerson);
        }
    }

    /**
     * 处理考勤记录
     *
     * @param recRecord 考勤记录
     */
    private void handlerRecRecord(RecRecord recRecord) {
        logger.info("MQTT处理考勤记录, recRecord:{}", recRecord);
        if (recRecord == null) {
            return;
        }
        EmpAttendSyncDataParam param = new EmpAttendSyncDataParam();
        String personGuid = recRecord.getCustomId();

        // (1)验证闸机-唯一标识
        String deviceKey = recRecord.getFacesluiceId();
        AppFaceGateDTO faceGateDTO = appFaceGateManager.getByDeviceKey(deviceKey);
        if (faceGateDTO == null) {
            logger.warn("考勤记录同步-闸机未入后台数据库, deviceKey:{} ", deviceKey);
            return;
        }
        // (2)验证时间-过滤重复记录
        Date time = recRecord.getTime();
        //从缓存中获取上一次打卡记录
        Date latestShowTime = faceGateAttendRedisDao.getShowTime(deviceKey, personGuid);
        if (latestShowTime != null && DateUtil.addMinutes(latestShowTime, INTERVAL_TIME).after(time)) {
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            logger.info("两次刷脸时间相同, time:{}, latestShowTime:{}", sdf.format(time), sdf.format(latestShowTime));
            return;
        }
        //保存刷脸时间到缓存中
        faceGateAttendRedisDao.setShowTime(deviceKey, personGuid, time);
        //保存考勤信息
        param.setDeviceKey(deviceKey);
        param.setPersonGuid(personGuid);
        param.setEmpName(recRecord.getPersionName());
        param.setDeptId(faceGateDTO.getDeptId());
        param.setFaceGateId(faceGateDTO.getFaceGateId());
        param.setTemperature(recRecord.getTemperature());
        //处理图片
        String pic = recRecord.getPic();
        if (StringUtils.isNotBlank(pic)) {
            String imgUrl = uploadImg(pic);
            param.setPicture(imgUrl);
        }
        param.setShowTime(time);
        param.setDirection(convertDirection(recRecord.getDirection()));
        param.setTemperature(recRecord.getTemperature());
        //推送消息到考勤记录中
        String jsonStr = JSON.toJSONString(param);
        amqpTemplate.convertAndSend(QueueConst.EMP_FACEGATE_ATTEND_DATA, jsonStr);

    }


    /**
     * 处理人员信息
     *
     * @param personInfo 人员信息
     */
    private void handlerPersonInfo(PersonInfo personInfo, String pic) {
        logger.info("MQTT保存人员信息, personInfo:{}", personInfo);
        if (personInfo == null) {
            return;
        }
        String deviceKey = personInfo.getFacesluiceId();
        AppFaceGateDTO faceGateDTO = appFaceGateManager.getByDeviceKey(deviceKey);
        if (faceGateDTO == null) {
            logger.warn("考勤记录同步-闸机未入后台数据库, deviceKey:{} ", deviceKey);
            return;
        }
        EmpInfoSyncDataParam param = new EmpInfoSyncDataParam();
        param.setSyncType(FaceGateSyncOp.EMP_ADD_OR_UPDATE.getValue());
        param.setImgType(EmpSyncImgType.BASE64.getValue());
        param.setDeptId(faceGateDTO.getDeptId());
        param.setEmpCode(personInfo.getCustomId());
        String name = null;
        if (StringUtils.isNotBlank(personInfo.getName())) {
            name = personInfo.getName().trim();
        }
        param.setEmpName(name);
        param.setGender(convertGender(personInfo.getGender()));
        String idCardNo = null;
        if (StringUtils.isNotBlank(personInfo.getIdCard())) {
            idCardNo = personInfo.getIdCard().trim();
        }
        param.setIdCardNo(idCardNo);
        String address = null;
        if (StringUtils.isNotBlank(personInfo.getAddress())) {
            address = personInfo.getAddress().trim();
        }
        param.setAddress(address);
        String telNum = null;
        if (StringUtils.isNotBlank(personInfo.getTelnum1())) {
            telNum = personInfo.getTelnum1().trim();
        }
        param.setPhone(telNum);
        if (StringUtils.isNotBlank(pic)) {
            //去掉图片前缀
            String prefix = "^data:image/\\w+;base64,";
            pic = pic.replaceAll(prefix, "");
            param.setAvatar(pic);
        }
        //推送消息
        String jsonStr = JSONUtil.toString(param);
        amqpTemplate.convertAndSend(QueueConst.EMP_FACEGATE_EMP_INFO, jsonStr);
        //置空人员查询数据
        MqttConfig.SEARCH_PERSON = null;
        // 解开锁
        LockSupport.unpark(MqttConfig.MQTT_THREAD);
    }


    /**
     * 处理人员列表信息
     *
     * @param person 人员信息
     */
    private void handlerPersonList(Person person) {
        logger.info("MQTT处理人员CustomId, person:{}", person);
        if (person == null) {
            return;
        }
        //获取人员唯一ID
        String customIds = person.getCustomId();
        if (StringUtils.isBlank(customIds)) {
            return;
        }
        //处理人员ID
        String[] customIdArrays = customIds.split(SzycConst.CUSTOM_ID_REGEX);
        //去重转换为集合
        List<String> customIdList = Arrays.stream(customIdArrays).distinct().collect(Collectors.toList());
        logger.info("闸机人数:{}", customIdList.size());
        //取出闸机ID
        String deviceKey = person.getFacesluiceId();
        AppFaceGateDTO faceGateDTO = appFaceGateManager.getByDeviceKey(deviceKey);
        if (faceGateDTO == null) {
            logger.warn("人员入库-闸机未入后台数据库, deviceKey:{} ", deviceKey);
            return;
        }
        //取出缓存的 empCode列表
        Integer deptId = faceGateDTO.getDeptId();
        List<String> empCodeList = mqttEmpRedisDao.getEmpCodeList(deptId);
        //去掉已保存的 empCode
        customIdList.removeAll(empCodeList);
        // 人员信息
        List<AppEmp> empList = new ArrayList<>();
        for (String customId : customIdList) {
            //保存查询人员详情消息到队列中
            saveSearchPersonToQueue(customId, deviceKey);
            //保存customId到缓存中
            empCodeList.add(customId);
            try {
                //验证是否存在
                AppEmp appEmp = appEmpMapper.selectByDeptIdAndEmpCode(deptId, customId);
                if (appEmp != null) {
                    continue;
                }
                appEmp = new AppEmp();
                appEmp.setDeptId(deptId);
                appEmp.setEmpCode(customId);
                empList.add(appEmp);
            } catch (Exception e) {
                logger.warn("保存人员失败, deptId:{}, empCode:{}", deptId, customId);
                empCodeList.remove(customId);
            }
        }
        //保存人员
        appEmpMapper.batchInsertEmp(empList);
        //保存到缓存中
        mqttEmpRedisDao.saveEmpCodeList(deptId, empCodeList);
    }

    /**
     * 保存查询人员到队列中
     *
     * @param customId 人员唯一标识
     */
    private void saveSearchPersonToQueue(String customId, String deviceKey) {
        String messageId = Long.toString(System.currentTimeMillis());
        Person p = new Person();
        p.setCustomId(customId);
        p.setPicture(1);
        SearchPerson searchPerson = new SearchPerson(messageId, p);
        String topic = MessageFormat.format(SzycConst.TOPIC_REQ, deviceKey);
        searchPerson.setTopic(topic);
        mqttEmpRedisDao.saveMqttTopic(searchPerson);
    }


    /**
     * 玉川-风潮 性别标识转换
     * 玉川性别字段标识  0-男  1-女
     * 风潮性别标识 1-男 2-女
     *
     * @param gender 玉川性别标识
     * @return 风潮性别标识
     */
    private Integer convertGender(String gender) {
        int sex = 1;
        if (gender != null && 1 == Integer.parseInt(gender)) {
            sex = 2;
        }
        return sex;
    }


    /**
     * 玉川-风潮 进出方向转换
     * 玉川方向标识  entr-进   exit-出
     * 风潮方向标识  1-进      0-出
     *
     * @param direction 玉川进出方向
     * @return 风潮进出方向
     */
    private Integer convertDirection(String direction) {
        int direc = 1;
        if (SzycConst.OUT.equals(direction)) {
            direc = 0;
        }
        return direc;
    }


    /**
     * 上传识别照片
     *
     * @param base64Str base64
     * @return 图片地址
     */
    private String uploadImg(String base64Str) {
        String imgUrl = "";
        if (StringUtils.isEmpty(base64Str)) {
            return imgUrl;
        }
        //去掉图片前缀
        String prefix = "^data:image/\\w+;base64,";
        base64Str = base64Str.replaceAll(prefix, "");
        try {
            byte[] imageByte = Base64Util.decode(base64Str);
            ByteArrayInputStream inputStream = new ByteArrayInputStream(imageByte);
            String path = filePathConfig.getFilePath("emp/faceGateRecord", RandomUtil.getRandomFileName(), "jpeg");
            imgUrl = fileHandler.upload(path, inputStream);
        } catch (Exception e) {
            logger.warn("考勤识别上传base64图片失败", e);
        }
        return imgUrl;
    }


}
